#ifndef __BB_DATA_HANDLE_H__
#define __BB_DATA_HANDLE_H__

#include <memory>
#include <string>
#include <unordered_map>

#include "../exception/exception.h"
#include "data_stream.h"

namespace bb::data {

class DataHandle {
  private:
    std::unordered_map<std::string, std::shared_ptr<data::DataStream>> inputs_;
    std::unordered_map<std::string, std::shared_ptr<data::DataStream>> outputs_;

    std::unordered_map<std::string, std::shared_ptr<data::DataStream>> variables_;
    std::string                                                        node_name_;

  public:
    void ConfigInput(std::string name, std::shared_ptr<data::DataStream> data) {
        if (not inputs_.count(name))
            inputs_.emplace(name, data);
        else
            throw exception::LogicError("Node [", node_name_, "]: input data stream [", name,
                                        "] already exist"
                                        "maybe you could use SetInput(name, data) by instead~");
    }
    void SetInput(std::string name, std::shared_ptr<data::DataStream> data) {
        if (inputs_.count(name))
            inputs_.at(name) = data;
        else
            throw exception::LogicError("Node [", node_name_, "]: input data stream [", name,
                                        "] not exist"
                                        "maybe you should ConfigInput(name, data) first!");
    }
    void ConfigOutput(std::string name, std::shared_ptr<data::DataStream> data) {
        if (not outputs_.count(name))
            outputs_.emplace(name, data);
        else
            throw exception::LogicError("Node [", node_name_, "]: output data stream [", name,
                                        "] already exist"
                                        "maybe you could use SetOutput(name, data) by instead~");
    }
    void SetOutput(std::string name, std::shared_ptr<data::DataStream> data) {
        if (outputs_.count(name))
            outputs_.at(name) = data;
        else
            throw exception::LogicError("Node [", node_name_, "]: output data stream [", name,
                                        "] not exist"
                                        "maybe you should ConfigOutput(name, data) first!");
    }
    void ConfigVariable(std::string name, std::shared_ptr<data::DataStream> data) {
        if (not variables_.count(name))
            variables_.emplace(name, data);
        else
            throw exception::LogicError("Node [", node_name_, "]: variable data stream [", name,
                                        "] already exist"
                                        "maybe you could use SetVariable(name, data) by instead~");
    }
    void SetVariable(std::string name, std::shared_ptr<data::DataStream> data) {
        if (variables_.count(name))
            variables_.at(name) = data;
        else
            throw exception::LogicError("Node [", node_name_, "]: variable data stream [", name,
                                        "] not exist"
                                        "maybe you should ConfigVariable(name, data) first!");
    }
    void ResetDataInputs() {
        for (auto [_, data] : inputs_) data->ResetToDefault();
    }
    void ResetDataOutputs() {
        for (auto [_, data] : outputs_) data->ResetToDefault();
    }
    void ResetDataVariables() {
        for (auto [_, data] : variables_) data->ResetToDefault();
    }
    auto InputsData() -> std::unordered_map<std::string, std::shared_ptr<data::DataStream>> { return inputs_; }
    auto InputData(std::string name) -> std::shared_ptr<data::DataStream> const {
        if (inputs_.count(name))
            return inputs_.at(name);
        else
            throw exception::RuntimeError("Node [", node_name_, "]: no such input data named ", name);
    }
    auto OutputsData() -> std::unordered_map<std::string, std::shared_ptr<data::DataStream>> const { return outputs_; }
    auto OutputData(std::string name) -> std::shared_ptr<data::DataStream> const {
        if (outputs_.count(name))
            return outputs_.at(name);
        else
            throw exception::RuntimeError("Node [", node_name_, "]: no such output data named ", name);
    }
    auto Variables() -> std::unordered_map<std::string, std::shared_ptr<data::DataStream>> const { return variables_; }
    auto Variable(std::string name) -> std::shared_ptr<data::DataStream> const {
        if (variables_.count(name))
            return variables_.at(name);
        else
            throw exception::RuntimeError("Node [", node_name_, "]: no such variable named ", name);
    }
};

} // namespace bb::data

#endif // __BB_DATA_HANDLE_H__